﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BitmapTagger
{
	/// <summary>
	/// Represents an unsigined rational number used in tags, 
	/// an unsigned rational is one uint divided by another uint
	/// </summary>
	public class URational
	{
		private uint numerator;
		private uint denominator;

		#region constructors

		/// <summary>
		/// Create a new unsigned rational
		/// </summary>
		/// <param name="encodedURational">A URational encoded as a UInt64, numerator as the low bits, denominator as the high bits</param>
		public URational(UInt64 encodedURational)
		{
			UInt64 converted = encodedURational & 0xFFFFFFFF00000000;

			denominator = (uint)(converted >> 32);

			converted = encodedURational & 0x00000000FFFFFFFF;

			numerator = (uint)converted;
		}

		/// <summary>
		/// Create a new unsigned rational
		/// </summary>
		/// <param name="numerator">The numerator (top number in division)</param>
		/// <param name="denominator">The denominator (bottom number in division)</param>
		public URational(uint numerator, uint denominator)
		{
			this.numerator = numerator;
			this.denominator = denominator;
		}


		/// <summary>
		/// Create a new unsigned rational from a string, see example for string formats
		/// </summary>
		/// <param name="urational">The unsigned rational in string format</param>
		/// <example>
		/// 78
		/// 1/600
		/// </example>
		public URational(string urational)
		{
			if (uint.TryParse(urational, out numerator))
			{
				denominator = 1;
			}
			else if (urational.Contains('/'))
			{
				char[] splitChar = new char[1] { '/' };
				string[] strings = urational.Split(splitChar, 2);

				if (!uint.TryParse(strings[0], out numerator))
					throw new FormatException("URational string not in proper format");
				if (!uint.TryParse(strings[1], out denominator))
					throw new FormatException("URational string not in proper format");

			}
			else
			{
				throw new FormatException("URational string not in proper format");
			}
		}

		#endregion

		/// <summary>
		/// Get the numerator
		/// </summary>
		public uint Numerator { get { return numerator; } }

		/// <summary>
		/// Get the denominators
		/// </summary>
		public uint Denominator { get { return denominator; } }

		#region conversions and castings

		/// <summary>
		/// Get a string that represents the URational
		/// </summary>
		/// <returns>Arepresents the rational </returns>
		public override string ToString()
		{
			return String.Format("{0}/{1}", Numerator, Denominator);
		}

		/// <summary>
		/// Explicit type casting from URational to string
		/// </summary>
		/// <param name="urational">The URational to cast as a string</param>
		/// <returns>The URational as a string</returns>
		public static explicit operator string(URational urational)
		{
			return urational.ToString();
		}

		/// <summary>
		/// Get the URational as a float (numerator / denominator)
		/// </summary>
		/// <returns>A float value representing theU Rational</returns>
		public float ToFloat()
		{
			return (float)Numerator / (float)Denominator;
		}

		/// <summary>
		/// Explicit type casting from URational to float
		/// </summary>
		/// <param name="urational">The URational to cast as a float</param>
		/// <returns>The URational as a float</returns>
		public static explicit operator float(URational urational)
		{
			return urational.ToFloat();
		}

		/// <summary>
		/// Get the URational encoded as an UInt64 (numerator as the low bits, denominator as high bits)
		/// </summary>
		/// <returns>The Rational encoded as an UInt64</returns>
		public UInt64 ToUInt64()
		{
			UInt64 numeratorConverted = (UInt64)Numerator;
			UInt64 denominatorConverted = (UInt64)Denominator;
			numeratorConverted = numeratorConverted & 0x00000000FFFFFFFF;
			denominatorConverted = denominatorConverted & 0x00000000FFFFFFFF;
			denominatorConverted = denominatorConverted << 32;

			return denominatorConverted | numeratorConverted;

		}

		/// <summary>
		/// Explicit type casting from URational to UInt64, encoded with numerator as the low bits, 
		/// denominator as the high bits
		/// </summary>
		/// <param name="urational">The URational to cast as an UInt64</param>
		/// <returns>The URational as an UInt64</returns>
		public static explicit operator UInt64(URational urational)
		{
			return urational.ToUInt64();
		}

		#endregion

	}
}
